"""
1. 使请使用Tensorflow实现实现XOR(异或)。（100分）
"""
# ①　导入必要的包。（8分）
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

XOR_X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=np.float32)
XOR_Y = np.array([[0], [1], [1], [0]], dtype=np.float32)
tf.random.set_random_seed(777)
np.random.seed(777)

# ②　分别定义x和y两个占位符，分别作为。（8分）
x = tf.placeholder(tf.float32, [None, 2], name='x')
y = tf.placeholder(tf.float32, [None, 1], name='y')

# ③　声明变量W1和W2。(8分）
N2 = 10
W1 = tf.Variable(tf.random.normal([2, N2]), dtype=tf.float32, name='W1')
W2 = tf.Variable(tf.random.normal([N2, 1]), dtype=tf.float32, name='W2')

# ④　声明变量b1和b2（8分）
b1 = tf.Variable(tf.random.normal([1, N2]), dtype=tf.float32, name='b1')
b2 = tf.Variable(tf.random.normal([1, 1]), dtype=tf.float32, name='b2')

# ⑤　进行计算，激活函数采用sigmoid函数。（8分）
z1 = tf.matmul(x, W1) + b1  # (m, N2)
a1 = tf.sigmoid(z1)  # (m, N2)
z2 = tf.matmul(a1, W2) + b2  # (m, 1)
a2 = tf.sigmoid(z2)  # (m, 1)

dz2 = a2 - y  # (m, 1)
da1 = tf.matmul(dz2, tf.transpose(W2))  # (m, N2)
dz1 = da1 * a1 * (1 - a1)  # (m, N2)

dW2 = tf.matmul(tf.transpose(a1), dz2) / tf.cast(tf.shape(a1)[0], dtype=tf.float32)  # (N2, 1)
db2 = tf.reduce_mean(dz2, axis=0)  # (1, 1)
dW1 = tf.matmul(tf.transpose(x), dz1) / tf.cast(tf.shape(x)[0], dtype=tf.float32)  # (2, N2)
db1 = tf.reduce_mean(dz1, axis=0)  # (1, N2)

# ⑥　定义损失函数，损失函数采用交叉熵。（8分）
j = tf.negative(
    (tf.matmul(tf.transpose(y), tf.log(a2)) + tf.matmul(tf.transpose(1 - y), tf.log(1 - a2)))
    / tf.cast(tf.shape(y)[0], dtype=tf.float32)
)

acc = tf.reduce_mean(
    tf.cast(
        tf.equal(a2 > 0.5, y > 0.5),
        tf.float32
    )
)

# ⑦　设置学习率，学习率设置为0.01。(8分）
ALPHA = 0.01

# ⑧　采用随机梯度优化算法。（8分）
update = [
    tf.assign(W1, W1 - ALPHA * dW1),
    tf.assign(W2, W2 - ALPHA * dW2),
    tf.assign(b1, b1 - ALPHA * db1),
    tf.assign(b2, b2 - ALPHA * db2),
]

# ⑨　生成会话，进行训练。（8分）
ITERS = 8500
GROUP = 100
cost_history = []
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    for i in range(ITERS):
        _, cost, accv = sess.run([update, j, acc], feed_dict={x: XOR_X, y: XOR_Y})
        cost = cost[0][0]
        cost_history.append(cost)
        # ⑩　每迭代100次打印结果
        if i % GROUP == 0:
            print(f'#{i + 1}: cost = {cost}, accv = {accv}')
    if i % GROUP != 0:
        print(f'#{i + 1}: cost = {cost}, accv = {accv}')

    # 11　打印所有运算结果。（8分）
    a2v = sess.run(a2, feed_dict={x: XOR_X})
    a2v = np.squeeze(a2v, axis=1)
    for iy, ia, iaa, ir in zip(XOR_Y, a2v, np.int8(a2v > 0.5), np.equal(XOR_Y.ravel() > 0.5, a2v > 0.5)):
        print(f'{iy}: {ia} => {iaa} ({ir})')

    # 12　绘制损失函数曲线（8分）
    plt.plot(cost_history)

# show all plotting
plt.show()
